﻿#include "HTTPClient.h"
#include <sstream>
#include <algorithm>
#include <thread>
#include <chrono>
#include <iostream>

#ifdef _WIN32
    #define CLOSE_SOCKET closesocket
    #define SOCK_ERROR SOCKET_ERROR
#else
    #define CLOSE_SOCKET close
    #define SOCK_ERROR -1
    typedef int SOCKET;
    const int INVALID_SOCKET = -1;
#endif


NahidaProject::HTTPClient::HTTPClient(const std::string& baseUrl) : baseUrl(baseUrl), timeoutSeconds(30) {
	InitalSocket();
}

NahidaProject::HTTPClient::~HTTPClient() {
    CleanupSocket();
}

bool NahidaProject::HTTPClient::InitalSocket() {
#ifdef _WIN32
    return WSAStartup(MAKEWORD(2, 2), &wsaData) == 0;
#else
    return true;
#endif
}

void NahidaProject::HTTPClient::CleanupSocket() {
#ifdef _WIN32
    WSACleanup();
#endif
}

std::string NahidaProject::HTTPClient::MethodToString(NahidaProject::ClientHTTPMethod method) const {
    switch (method) {
		case NahidaProject::ClientHTTPMethod::GET: return "GET";
        case NahidaProject::ClientHTTPMethod::POST: return "POST";
        case NahidaProject::ClientHTTPMethod::PUT: return "PUT";
		case NahidaProject::ClientHTTPMethod::DELETE_METHOD: return "DELETE";
        case NahidaProject::ClientHTTPMethod::HEAD: return "HEAD";
        case NahidaProject::ClientHTTPMethod::OPTIONS: return "OPTIONS";
        default: return "GET";
    }
}

void NahidaProject::HTTPClient::SetBaseURI(const std::string& url) {
    baseUrl = url;
}

void NahidaProject::HTTPClient::SetDefaultHeader(const std::string& key, const std::string& value) {
    defaultHeaders[key] = value;
}

void NahidaProject::HTTPClient::SetTimeout(int seconds) {
    timeoutSeconds = seconds;
}

std::pair<std::string, std::string> NahidaProject::HTTPClient::ParseURI(const std::string& url) const {
    std::string fullUrl = url;
    if (!baseUrl.empty() && url.find("://") == std::string::npos) {
        fullUrl = baseUrl + url;
    }
    
    // 解析URL格式: http://host:port/path 或 https://host:port/path
    size_t protocolEnd = fullUrl.find("://");
    if (protocolEnd == std::string::npos) {
        return {"", ""};
    }
    
    std::string protocol = fullUrl.substr(0, protocolEnd);
    size_t hostStart = protocolEnd + 3;
    size_t pathStart = fullUrl.find('/', hostStart);
    
    std::string hostPort, path;
    if (pathStart != std::string::npos) {
        hostPort = fullUrl.substr(hostStart, pathStart - hostStart);
        path = fullUrl.substr(pathStart);
    } else {
        hostPort = fullUrl.substr(hostStart);
        path = "/";
    }
    
    return {hostPort, path};
}

std::string NahidaProject::HTTPClient::BuildHTTPRequest(const std::string& method, const std::string& url, const std::map<std::string, std::string>& customHeaders, const std::string& body) const {
    auto [hostPort, path] = ParseURI(url);
    if (hostPort.empty()) {
        return "";
    }

    std::ostringstream request;
    request << method << " " << path << " HTTP/1.1\r\n";
    request << "Host: " << hostPort << "\r\n";
    
    // 添加默认头部
    for (const auto& header : defaultHeaders) {
        request << header.first << ": " << header.second << "\r\n";
    }
    
    // 添加自定义头部
    for (const auto& header : customHeaders) {
        request << header.first << ": " << header.second << "\r\n";
    }
    
    // 如果有请求体，添加Content-Length头部
    if (!body.empty()) {
        request << "Content-Length: " << body.length() << "\r\n";
    }
    
    request << "Connection: close\r\n";
    request << "\r\n";
    
    if (!body.empty()) {
        request << body;
    }
    
    return request.str();
}

NahidaProject::ClientHTTPResponse NahidaProject::HTTPClient::ParseHTTPResponse(const std::string& response) const {
    ClientHTTPResponse httpResponse;
    
    if (response.empty()) {
        return httpResponse;
    }
    
    size_t headerEnd = response.find("\r\n\r\n");
    if (headerEnd == std::string::npos) {
        return httpResponse;
    }
    
    std::string headerPart = response.substr(0, headerEnd);
    httpResponse.body = response.substr(headerEnd + 4);
    
    std::istringstream headerStream(headerPart);
    std::string line;
    
    // 解析状态行
    if (std::getline(headerStream, line)) {
        size_t firstSpace = line.find(' ');
        if (firstSpace != std::string::npos) {
            size_t secondSpace = line.find(' ', firstSpace + 1);
            if (secondSpace != std::string::npos) {
                httpResponse.statusCode = std::stoi(line.substr(firstSpace + 1, secondSpace - firstSpace - 1));
                httpResponse.statusMessage = line.substr(secondSpace + 1);
            }
        }
    }
    
    // 解析头部字段
    while (std::getline(headerStream, line)) {
        size_t colonPos = line.find(':');
        if (colonPos != std::string::npos) {
            std::string key = line.substr(0, colonPos);
            std::string value = line.substr(colonPos + 2); // 跳过冒号和空格
            
            // 去除key和value的空白字符
            key.erase(0, key.find_first_not_of(" \t"));
            key.erase(key.find_last_not_of(" \t") + 1);
            value.erase(0, value.find_first_not_of(" \t"));
            value.erase(value.find_last_not_of(" \t") + 1);
            
            httpResponse.headers[key] = value;
        }
    }
    
    return httpResponse;
}

NahidaProject::ClientHTTPResponse NahidaProject::HTTPClient::Request(ClientHTTPMethod method, const std::string& url,const std::string& body, const std::map<std::string, std::string>& headers) {
    ClientHTTPResponse response;
    
    auto [hostPort, path] = ParseURI(url);
    if (hostPort.empty()) {
        response.statusCode = 0;
        response.statusMessage = "Invalid URL";
        return response;
    }
    
    // 解析主机和端口
    std::string host = hostPort;
    std::string port = "80";
    size_t colonPos = hostPort.find(':');
    if (colonPos != std::string::npos) {
        host = hostPort.substr(0, colonPos);
        port = hostPort.substr(colonPos + 1);
    }
    
    // 创建socket
    SOCKET sock = socket(AF_INET, SOCK_STREAM, 0);
    if (sock == INVALID_SOCKET) {
        response.statusCode = 0;
        response.statusMessage = "Failed to create socket";
        return response;
    }
    
    // 设置超时
#ifdef _WIN32
    DWORD timeout = timeoutSeconds * 1000;
    setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (const char*)&timeout, sizeof(timeout));
    setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, (const char*)&timeout, sizeof(timeout));
#else
    struct timeval tv;
    tv.tv_sec = timeoutSeconds;
    tv.tv_usec = 0;
    setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof(tv));
    setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, (const char*)&tv, sizeof(tv));
#endif
    
    // 解析主机地址
    struct hostent* hostEntry = gethostbyname(host.c_str());
    if (hostEntry == nullptr) {
        CLOSE_SOCKET(sock);
        response.statusCode = 0;
        response.statusMessage = "Failed to resolve hostname";
        return response;
    }
    
    struct sockaddr_in serverAddr;
    serverAddr.sin_family = AF_INET;
    serverAddr.sin_port = htons(std::stoi(port));
    serverAddr.sin_addr = *((struct in_addr*)hostEntry->h_addr);
    
    // 连接到服务器
    if (connect(sock, (struct sockaddr*)&serverAddr, sizeof(serverAddr)) == SOCK_ERROR) {
        CLOSE_SOCKET(sock);
        response.statusCode = 0;
        response.statusMessage = "Failed to connect to server";
        return response;
    }
    
    // 构建HTTP请求
    std::string httpRequest = BuildHTTPRequest(MethodToString(method), url, headers, body);
    if (httpRequest.empty()) {
        CLOSE_SOCKET(sock);
        response.statusCode = 0;
        response.statusMessage = "Failed to build HTTP request";
        return response;
    }
    
    // 发送请求
    if (send(sock, httpRequest.c_str(), httpRequest.length(), 0) == SOCK_ERROR) {
        CLOSE_SOCKET(sock);
        response.statusCode = 0;
        response.statusMessage = "Failed to send request";
        return response;
    }
    
    // 接收响应
    std::string httpResponse;
    char buffer[4096];
    int bytesReceived;
    
    while ((bytesReceived = recv(sock, buffer, sizeof(buffer) - 1, 0)) > 0) {
        buffer[bytesReceived] = '\0';
        httpResponse += buffer;
    }
    
    CLOSE_SOCKET(sock);
    
    // 解析响应
    return ParseHTTPResponse(httpResponse);
}

NahidaProject::ClientHTTPResponse NahidaProject::HTTPClient::Get(const std::string& url, const std::map<std::string, std::string>& headers) {
    return Request(ClientHTTPMethod::GET, url, "", headers);
}

NahidaProject::ClientHTTPResponse NahidaProject::HTTPClient::Post(const std::string& url, const std::string& body, const std::map<std::string, std::string>& headers) {
    return Request(ClientHTTPMethod::POST, url, body, headers);
}

NahidaProject::ClientHTTPResponse NahidaProject::HTTPClient::Put(const std::string& url, const std::string& body, const std::map<std::string, std::string>& headers) {
    return Request(ClientHTTPMethod::PUT, url, body, headers);
}

NahidaProject::ClientHTTPResponse NahidaProject::HTTPClient::Delete(const std::string& url, const std::map<std::string, std::string>& headers) {
    return Request(ClientHTTPMethod::DELETE_METHOD, url, "", headers);
}

void NahidaProject::HTTPClient::AsyncRequest(ClientHTTPMethod method, const std::string& url, std::function<void(const ClientHTTPResponse&)> callback, const std::string& body, const std::map<std::string, std::string>& headers) {
    std::thread([this, method, url, callback, body, headers]() {
		ClientHTTPResponse response = this->Request(method, url, body, headers);
        callback(response);
    }).detach();
}
